#include "TimeSliceCalculations.h"
#include <iostream>


TimeSliceCalculations::TimeSliceCalculations(SystemView* v){
	view = v;
	decent_constant = 1.0;
}

TimeSliceCalculations::~TimeSliceCalculations(void)
{
	
}

void TimeSliceCalculations::responseTime(vector<ExtentStatsAlgo*>* ssdExtents, vector<ExtentStatsAlgo*>* fcExtents, vector<ExtentStatsAlgo*>* sataExtents, int time, DeviceData* ssd, DeviceData* fc, DeviceData* sata, double ssdUtil[], double fcUtil[], double sataUtil[], int type){
	map<LUNID, DeviceArray> extentDevices;		// for each lun there is an array that includes the number of read misses of that lun in each device type
	map<LUNID, DeviceArray>::iterator lunIter;
	ExtentStatsAlgo* ee;
	double rm, kb;
	LUNID lid;

	// 3 while loops - for each lun counts how many extents in each device type
	// extents in ssd devices
	vector<ExtentStatsAlgo*>::iterator extentIter = (*ssdExtents).begin();
	while(extentIter!=(*ssdExtents).end()){
		ee = (*extentIter);
		ExtentStatistics* s = ee->getStat(time);
		if (s!=0){
			rm = s->getStat(rand_rm);//+s->getStat(seq_r);
			kb = s->getStat(kilo_rm);//+s->getStat(seq_kilo_r);
			lid = (*extentIter)->getExtent()->getLun()->getM_uid();
			lunIter=extentDevices.find(lid);
			if (lunIter!=extentDevices.end()){
				(*lunIter).second.a[0].readMiss+=rm;
				(*lunIter).second.a[0].kbRead+=kb;
			}
			else{
				DeviceStruct a;
				a.readMiss= rm;
				a.kbRead=kb;
				DeviceStruct b;
				b.readMiss= 0;
				b.kbRead= 0;
				DeviceStruct c;
				c.readMiss= 0;
				c.kbRead=0;
				DeviceArray devices;
				devices.a[0]=a;
				devices.a[1]=b;
				devices.a[2]=c;
				extentDevices.insert(pair<LUNID,DeviceArray>(lid, devices));
			}
		}
		extentIter++;
	}
	
	// extents in fc devices
	extentIter = (*fcExtents).begin();
	while(extentIter!=(*fcExtents).end()){
		ee = (*extentIter);
		ExtentStatistics* s = ee->getStat(time);
		if (s!=0){
			rm = s->getStat(rand_rm);//+s->getStat(seq_r);
			kb = s->getStat(kilo_rm);//+s->getStat(seq_kilo_r);
			lid = (*extentIter)->getExtent()->getLun()->getM_uid();
			lunIter=extentDevices.find(lid);
			if (lunIter!=extentDevices.end()){
				(*lunIter).second.a[1].readMiss+=rm;
				(*lunIter).second.a[1].kbRead+=kb;
			}
			else{
				DeviceStruct a;
				a.readMiss= 0;
				a.kbRead=0;
				DeviceStruct b;
				b.readMiss= rm;
				b.kbRead= kb;
				DeviceStruct c;
				c.readMiss= 0;
				c.kbRead=0;
				DeviceArray devices;
				devices.a[0]=a;
				devices.a[1]=b;
				devices.a[2]=c;
				extentDevices.insert(pair<LUNID,DeviceArray>(lid, devices));
			}
		}
		extentIter++;
	}

	// extents in fc devices
	extentIter = (*sataExtents).begin();
	while(extentIter!=(*sataExtents).end()){
		ee = (*extentIter);
		ExtentStatistics* s = ee->getStat(time);
		if (s!=0){
			rm = s->getStat(rand_rm);//+s->getStat(seq_r);
			kb = s->getStat(kilo_rm);//+s->getStat(seq_kilo_r);
			lid = (*extentIter)->getExtent()->getLun()->getM_uid();
			lunIter=extentDevices.find(lid);
			if (lunIter!=extentDevices.end()){
				(*lunIter).second.a[2].readMiss+=rm;
				(*lunIter).second.a[2].kbRead+=kb;
			}
			else{
				DeviceStruct a;
				a.readMiss= 0;
				a.kbRead=0;
				DeviceStruct b;
				b.readMiss= 0;
				b.kbRead= 0;
				DeviceStruct c;
				c.readMiss= rm;
				c.kbRead=kb;
				DeviceArray devices;
				devices.a[0]=a;
				devices.a[1]=b;
				devices.a[2]=c;
				extentDevices.insert(pair<LUNID,DeviceArray>(lid, devices));
			}
		}
		extentIter++;
	}

	// calculate response time for each lun for this time slice
	map<LUNID, LunTotalStatistics*>* luns = view->getMy_luns();
	map<LUNID, LunTotalStatistics*>::iterator statsIter;
	lunIter=extentDevices.begin();
	while(lunIter!=extentDevices.end()){
		lid = (*lunIter).first;
		statsIter=luns->find(lid);
		if (statsIter!=luns->end()){
			LunTotalStatistics* stats = (*statsIter).second;
			LunTimeStatistics* timeStats =stats->getLunTimeStatistics(time);
			if (timeStats != 0){
				if (type==0)
					timeStats->calcResponseTime((*lunIter).second.a[0].readMiss, (*lunIter).second.a[0].kbRead, (*lunIter).second.a[1].readMiss, (*lunIter).second.a[1].kbRead, (*lunIter).second.a[2].readMiss, (*lunIter).second.a[2].kbRead, ssd, fc, sata, (*statsIter).second->getLun()->getPriorityGroup());
				else 
					timeStats->calcResponseTimeGD((*lunIter).second.a[0].readMiss, (*lunIter).second.a[0].kbRead, (*lunIter).second.a[1].readMiss, (*lunIter).second.a[1].kbRead, (*lunIter).second.a[2].readMiss, (*lunIter).second.a[2].kbRead, ssd, fc, sata, ssdUtil, fcUtil, sataUtil, (*statsIter).second->getLun()->getPriorityGroup());
			}
		}
		lunIter++;
	}
}


double TimeSliceCalculations::sum_damage(int time){
	double damage = 0;
	map<LUNID, LunTotalStatistics*>* luns = view->getMy_luns();
	double response_time;

	map<LUNID, LunTotalStatistics*>::iterator lun_it;
	for(lun_it = luns->begin(); lun_it != luns->end(); lun_it++){
		Lun* lun = lun_it->second->getLun();
		if (lun_it->second->getLunTimeStatistics(time) != 0) {
			response_time = lun_it->second->getLunTimeStatistics(time)->getResponseTime();
			if (response_time != 0){
				damage += lun->calc_damage(response_time);
			}
		}
	}
	return damage;
}